package ru.myrtle.umf.util;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;

import ru.myrtle.umf.UObject;

public class UObjectList<T extends UObject> implements UList<T> {

    private class IteratorImpl<T extends UObject> implements Iterator<T> {

        private final Iterator<T> iterator;

        private IteratorImpl(Iterator<T> iterator) {
            this.iterator = iterator;
        }

        private T object;

        @Override
        public boolean hasNext() {
            return iterator.hasNext();
        }

        @Override
        public T next() {
            return (object = iterator.next());
        }

        @Override
        public void remove() {
            iterator.remove();

            objectRemoved(
                    object
            );
        }
    }

    /*
     *
     */

    private class ListIteratorImpl<T extends UObject> implements ListIterator<T> {

        private final ListIterator<T> iterator;

        private ListIteratorImpl(ListIterator<T> iterator) {
            this.iterator = iterator;
        }

        private T object;

        @Override
        public boolean hasNext() {
            return iterator.hasNext();
        }

        @Override
        public T next() {
            return (object = iterator.next());
        }

        @Override
        public boolean hasPrevious() {
            return iterator.hasPrevious();
        }

        @Override
        public T previous() {
            return (object = iterator.previous());
        }

        @Override
        public int nextIndex() {
            return iterator.nextIndex();
        }

        @Override
        public int previousIndex() {
            return iterator.previousIndex();
        }

        @Override
        public void remove() {
            iterator.remove();

            objectRemoved(
                    object
            );
        }

        @Override
        public void set(T t) {
            throw new RuntimeException("Not implemented"); // todo
        }

        @Override
        public void add(T t) {
            throw new RuntimeException("Not implemented"); // todo
        }
    }

    /*
     *
     */

    private final List<T> store;

    /*
     *
     */

    public UObjectList() {
        this.store = new ArrayList<T>();
    }

    public UObjectList(Collection<? extends T> c) {
        this();
        addAll(c);
    }

    UObjectList(List<T> store) {
        this.store = store;
    }

    /*
     *
     */

    protected void objectAdded(UObject object) {
    }

    protected void objectRemoved(UObject object) {
    }

    /*
     *
     */

    public int basicSize() {
        return store.size();
    }

    @Override
    public final int size() {
        return basicSize();
    }

    public boolean basicIsEmpty() {
        return store.isEmpty();
    }

    @Override
    public final boolean isEmpty() {
        return basicIsEmpty();
    }

    public boolean basicContains(Object o) {
        return store.contains(o);
    }

    @Override
    public final boolean contains(Object o) {
        return basicContains(o);
    }

    public Iterator<T> basicIterator() {
        return new IteratorImpl<T>(
                store.iterator()
        );
    }

    @Override
    public final Iterator<T> iterator() {
        return basicIterator();
    }

    public Object[] basicToArray() {
        return store.toArray();
    }

    @Override
    public final Object[] toArray() {
        return basicToArray();
    }

    public <T> T[] basicToArray(T[] a) {
        return store.toArray(a);
    }

    @Override
    public <T> T[] toArray(T[] a) {
        return basicToArray(a);
    }

    public boolean basicAdd(T e) {
        return store.add(e);
    }

    @Override
    public boolean add(T e) {
        boolean result = basicAdd(e);

        if (result) {
            objectAdded(e);
        }

        return result;
    }

    public boolean basicRemove(Object o) {
        return store.remove(o);
    }

    @Override
    public boolean remove(Object o) {
        boolean result = basicRemove(o);

        if (result) {
            objectRemoved((UObject) o);
        }

        return result;
    }

    public boolean basicContainsAll(Collection<?> c) {
        return store.containsAll(c);
    }

    @Override
    public boolean containsAll(Collection<?> c) {
        return basicContainsAll(c);
    }

    public boolean basicAddAll(Collection<? extends T> c) {
        boolean result = false;

        for (T element : c) {
            result |= basicAdd(element);
        }

        return result;
    }

    @Override
    public boolean addAll(Collection<? extends T> c) {
        boolean result = false;

        for (T element : c) {
            result |= add(element);
        }

        return result;
    }

    public boolean basicAddAll(int index, Collection<? extends T> c) {
        for (T element : c) {
            basicAdd(index++, element);
        }

        return true;
    }

    @Override
    public boolean addAll(int index, Collection<? extends T> c) {
        boolean result = basicAddAll(index, c);

        if (result) {
            for (UObject uObject : c) {
                objectAdded(uObject);
            }
        }

        return result;
    }

    public boolean basicRemoveAll(Collection<?> c) {
        boolean result = false;

        for (Object element : c) {
            result |= basicRemove(element);
        }

        return result;
    }

    @Override
    public boolean removeAll(Collection<?> c) {
        boolean result = false;

        for (Object element : c) {
            result |= remove(element);
        }

        return result;
    }

    public boolean basicRetainAll(Collection<?> c) {
        boolean result = false;

        for (Iterator<T> iterator = store.iterator(); iterator.hasNext();) {
            UObject element = iterator.next();

            if (c.contains(element)) {
                continue;
            }
            else {
                iterator.remove();
                result = true;
            }
        }

        return result;
    }

    @Override
    public boolean retainAll(Collection<?> c) {
        boolean result = false;

        for (Iterator<T> iterator = store.iterator(); iterator.hasNext();) {
            UObject o = iterator.next();

            if (c.contains(o)) {
                continue;
            }
            else {
                objectRemoved(o);
                iterator.remove();

                result = true;
            }
        }

        return result;
    }

    public void basicClear() {
        store.clear();
    }

    @Override
    public void clear() {
        for (UObject o : store) {
            objectRemoved(o);
        }

        basicClear();
    }

    public T basicGet(int index) {
        return store.get(index);
    }

    @Override
    public T get(int index) {
        return basicGet(index);
    }

    public T basicSet(int index, T element) {
        return store.set(index, element);
    }

    @Override
    public T set(int index, T element) {
        T previous = basicSet(index, element);

        objectRemoved(previous);
        objectAdded(element);

        return previous;
    }

    public void basicAdd(int index, T element) {
        store.add(
                index, element
        );
    }

    @Override
    public void add(int index, T element) {
        basicAdd(
                index, element
        );
        objectAdded(element);
    }

    public T basicRemove(int index) {
        return store.remove(
                index
        );
    }

    @Override
    public T remove(int index) {
        T removed = basicRemove(
                index
        );
        objectRemoved(removed);

        return removed;
    }

    public int basicIndexOf(Object o) {
        return store.indexOf(o);
    }

    @Override
    public int indexOf(Object o) {
        return basicIndexOf(o);
    }

    public int basicLastIndexOf(Object o) {
        return store.lastIndexOf(o);
    }

    @Override
    public int lastIndexOf(Object o) {
        return basicLastIndexOf(o);
    }

    public ListIterator<T> basicListIterator() {
        return new ListIteratorImpl<T>(
                store.listIterator()
        );
    }

    @Override
    public ListIterator<T> listIterator() {
        return basicListIterator();
    }

    public ListIterator<T> basicListIterator(int index) {
        return new ListIteratorImpl<T>(
                store.listIterator(index)
        );
    }

    @Override
    public ListIterator<T> listIterator(int index) {
        return basicListIterator(index);
    }

    public UList<T> basicSubList(int fromIndex, int toIndex) {
        return new UObjectList<T>(
                store.subList(fromIndex, toIndex)
        );
    }

    @Override
    public UList<T> subList(int fromIndex, int toIndex) {
        return basicSubList(fromIndex, toIndex);
    }

    @Override
    public String toString() {
        return store.toString();
    }
}
